#ifndef BOARDGUI_H
#define BOARDGUI_H

#include <iostream>
#include <QObject>
#include <QDebug>

#include "boardDefinitions.h"
#include "bitutils.h"
#include "board.h"
#include "movegenerator.h"
#include "search.h"

#include <QTest>

#include <QThread>

class Xsleep : public QThread
{
public:
    static void msleep(int ms)
    {
        QThread::msleep(ms);
    }
};

/*
  The chess board gui with qml
  */
class BoardGUI : public QObject
{
    Q_OBJECT
    // the NOTIFY signal is needed to inform about changes
      // all properties will be exposed to QML
    Q_PROPERTY(QString status READ status WRITE setStatus NOTIFY statusChanged)
    //Q_PROPERTY(bool marked READ isMarked WRITE setMarked NOTIFY markedChanged)

public:
    // constructor
    // - set reference to board and move generator
    BoardGUI(Board & board, MoveGenerator & moveGenerator, Search & search)
        : board_(board), moveGenerator_(moveGenerator), search_(search)
    {
        moveFromIndex = -1;
    }

    QString status() const { return chessPiecesString; }
    void setStatus(QString &status)
    {
      chessPiecesString = status;
      qDebug() << "gui status changed:" + chessPiecesString ;
      emit statusChanged();
    }

    //void setMarked(bool value) {m_marked = value; emit markedChanged();}
    //bool isMarked() {return m_marked;}

private:
    // reference to board
    Board & board_;
    // reference to move generator
    MoveGenerator & moveGenerator_;

    // reference to search
    Search & search_;

    // *** methods ***
    // update on GUI/or other interface
    void update();
    void markSquares(U64);
    void showTargetSquares(U64);

    // status property - internal qml string representation of board setup
    QString chessPiecesString;
    // this is better qt style for property marked;
    //bool m_marked;
    // gui moving a piece in play-mode
    int moveFromIndex;
    Move searchedMove;

public slots:
    void initSlot() {
        board_.init();

        board_.updateAdditionalBitboards();

        board_.sideToMove = WHITE_TO_MOVE;
        moveGenerator_.setBoard(board_);
        moveGenerator_.initAttacks();
        moveGenerator_.initMasks();
        //getMoves();
        update();
    }

    size_t qmlToBoardIndex(int qml_board_index) {
        // do some highlighting
        // - translate qml_board_index into board's move.from
        size_t file = qml_board_index % 8;
        size_t rank = qml_board_index / 8;
        size_t newRank = (7-rank);
        return file+8*newRank; // move.from index
    }

    // show all moves (and move!) for selected piece on "clicked" square (given by qml_board_index)
    void showAndMove(int qml_board_index) {
        moveGenerator_.generateMoves();
        size_t boardIndex = qmlToBoardIndex(qml_board_index);

        qDebug() << "INDEXCLICKED";
        qDebug() << qml_board_index;

        if (moveFromIndex >= 0) {
            // try a move
            Move myMove;
            // to clicked square
            myMove.from = moveFromIndex;
            myMove.to = boardIndex;

            // have clicked on same piece - this means we want to change move
            if (myMove.from == myMove.to) {
                // unpaint marked target squares
                update();

                // returning and waiting for next user move
                moveFromIndex = -1;
                return;
            }

            // - search moveList for all move.from's and to's and copy found move
            for (size_t i=0; i < moveGenerator_.getMoveListSize(); i++) {
                Move move = moveGenerator_.getMoveList().at(i);
                if ((move.from == myMove.from) && (move.to == myMove.to)) {
                    myMove.piece = move.piece;
                    myMove.captured = move.captured;
                    myMove.castling = move.castling;
                    myMove.promotion = move.promotion;
                    break;
                }
            }
            if (myMove.piece > 0) // we have found a valid movePiece from within moveGenList
            {
                search_.getMoveMaker().doMove(myMove);
                board_.updateAdditionalBitboards();
                update();
                qDebug() << "moved a piece";
                qDebug() << myMove.from;
                qDebug() << myMove.to;
                qDebug() << myMove.piece;


                // now do a search and computer move
                search();
                search_.getMoveMaker().doMove(searchedMove);
                board_.updateAdditionalBitboards();
                update();
                // after search
                board_.sideToMove = WHITE_TO_MOVE;

                // returning and waiting for next user move
                moveFromIndex = -1;
            }
            return;
        }

        // - search moveList for all move.from's and mark all corresponding move.to squares
        std::vector<size_t> moveToVector;
        for (size_t i=0; i < moveGenerator_.getMoveListSize(); i++) {
            Move move = moveGenerator_.getMoveList().at(i);
            if (move.from == boardIndex) {
                moveToVector.push_back(move.to);
            }
        }
        // now found all moveTo's and have it in vector -> visualize!
        qDebug() << "Possible Moves for piece on square: " + QString(SQUARENAME[boardIndex]);
        qDebug() << moveGenerator_.getMoveListSize();
        qDebug() << moveToVector.size();

        // build visualized target square bitboard
        // an overlay for chessFont -- see method
        U64 targetSquares = 0x0ULL;
        for (size_t i=0; i < moveToVector.size(); i++) {
            targetSquares |= Bitutils::Bit(moveToVector.at(i));
            qDebug() << SQUARENAME[moveToVector.at(i)];
        }

        showTargetSquares(targetSquares);
        moveFromIndex = qmlToBoardIndex(qml_board_index);
    }

     void search() {
         search_.init();
         search_.setSearchDepth(4);

         // recursive negamax search (saves bestMoveLine and its score value)
         qDebug() << "Launch Search";
         QTime myTimer; myTimer.start();
            //int score = search_.negaMax(search_.getSearchDepth(), -INFINITY, +INFINITY);
            int score = search_.chooseRandomMove();
         int nMilliseconds = myTimer.elapsed() + 1;
         unsigned long nodesPerSecond = search_.nodeCounter * 1000 / nMilliseconds;

         qCritical() << "total nodes visited";
         qCritical() << search_.nodeCounter;
         qCritical() << "nodes per second";
         qCritical() << nodesPerSecond;
         qCritical() << "nMilliseconds";
         qCritical() << nMilliseconds;

         QString outputMoveLine;
         for (int i=search_.getSearchDepth(); i>0; i--) {
             Move move(search_.currentBestMoveLine.at(i));
             outputMoveLine.append(" # " + QString(SQUARENAME[move.from]) + "-" + QString(SQUARENAME[move.to]));
         }
         searchedMove = search_.currentBestMoveLine.at(search_.getSearchDepth());

         qCritical() << "Search completed : Best MoveLine :  " + outputMoveLine;
         qCritical() << "With best score : ";
         qCritical() << score;
     }

     void perft() {
         int searchDepth = 4;

         for (int i=1; i <= searchDepth; i++) {
             initSlot();
             search_.init();
             search_.setSearchDepth(i);
             search_.setStartSideToMove(WHITE_TO_MOVE);

             QTime myTimer; myTimer.start();
                U64 score = search_.perft(0,i);
             int nMilliseconds = myTimer.elapsed() + 1;

             qCritical() << "nMilliseconds";
             qCritical() << nMilliseconds;
             qCritical() << "Depth";
             qCritical() << i;
             qCritical() << "Perft value: " + i;
             qCritical() << score;
         }

     }

signals:
        void statusChanged();
        //void markedChanged();


};

#endif // BOARDGUI_H
